package 中等.动态规划.数组区间;

import java.util.Arrays;

/**
 * 给定一个包含非负整数的 m x n 网格 grid ，请找出一条从左上
 * 角到右下角的路径，使得路径上的数字总和为最小。
 * 说明：一个机器人每次只能向下或者向右移动一步。
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode.cn/problems/0i0mDW
 */
public class 最小路径之和_offer099 {

    public static void main(String[] args) {

        最小路径之和_offer099 impl = new 最小路径之和_offer099();

        System.out.println(impl.minPathSum2(new int[][]{
                {1, 3, 1}, {1, 5, 1}, {4, 2, 1}
        }));

    }

    /**
     * 动态规划
     * 1，子问题：
     * 到达 dp[i][j] 的最小数字总和是多少
     * 2，状态转移方程：
     * dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1]) + grid[i][j]
     */
    public int minPathSum(int[][] grid) {
        int[][] dp = new int[grid.length][grid[0].length];
        dp[0][0] = grid[0][0];
        for (int i = 1; i < grid.length; i++) {
            dp[i][0] = dp[i - 1][0] + grid[i][0];
        }
        for (int j = 1; j < grid[0].length; j++) {
            dp[0][j] = dp[0][j - 1] + grid[0][j];
        }

        for (int i = 1; i < grid.length; i++) {
            for (int j = 1; j < grid[0].length; j++) {
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
        return dp[grid.length - 1][grid[0].length - 1];
    }


    private int[][] visited;

    /**
     * 回溯+记忆化+剪枝（超时）
     */
    public int minPathSum2(int[][] grid) {
        visited = new int[grid.length][grid[0].length];
        for (int i = 0; i < visited.length; i++) {
            Arrays.fill(visited[i], Integer.MAX_VALUE);
        }

        dfs(0, 0, 0, grid);
        return visited[visited.length - 1][visited[0].length - 1];
    }

    private void dfs(int i, int j, int curSum, int[][] grid) {
        if (i == grid.length || j == grid[0].length) {
            return;
        }
        curSum += grid[i][j];
        if (curSum > visited[i][j]) {
            return;
        }
        visited[i][j] = curSum;

        dfs(i + 1, j, curSum, grid);
        dfs(i, j + 1, curSum, grid);
    }

}
